; rdmem.asm
; FE3 RAM global definitions

	.include "fe3def.inc"
	.include "rdglobal.inc"

	.export md_init
	.export md_addbank
	.export md_alloc
	.export md_walk
	.export md_addblk
	.export md_next
	.export md_free

	.export free_cnt

	.segment "LDATA"
	
blk_cur:	.word 0
blk_nxt:	.word 0
	
free_bank:	.byte 0
free_adr:	.byte 0
free_cnt: 	.word 0

save_pg:	.res 1

chn_bank:	.res 1
chn_adr:	.res 1 

	BANK_TABLE_BASE = $2000
	ADR_TABLE_BASE  = $2080

	.segment "LCODE"
	
; Initialise memory management
md_init:	pha
			lda #0
			sta free_cnt
			sta free_cnt+1
			sta free_bank
			sta free_adr
			pla
			rts

; set new bank (A) & save current bank
set_bank:	ldx FE3_REG1
			stx save_pg
			ora #SUPER_RAM_MODE
			sta FE3_REG1
			rts

; reset bank
reset_bank:	lda save_pg
			ora #SUPER_RAM_MODE
			sta FE3_REG1
			rts

; Add a Super RAM bank to the free page list 
;	A ... bank index
;	(X,Y ... Scratch)
;
md_addbank:	; save caller's mem bank and switch to bank [A]
			pha
			jsr set_bank
			; chain banks
			ldx free_bank
			stx BANK_TABLE_BASE+$7F
			ldx #$7E
@bnk_loop:	sta BANK_TABLE_BASE, x
			dex
			bne @bnk_loop
			sta free_bank
			; chain addresses
			lda free_adr
			sta ADR_TABLE_BASE+$7F
			ldy #$BF
			ldx #$1F
@adh_loop:	tya
			sta ADR_TABLE_BASE+$5F, x
			dey
			dex
			bpl @adh_loop
			ldy #$7F
			ldx #$5E
@adl_loop:	tya
			sta ADR_TABLE_BASE, x
			dey
			dex
			bne @adl_loop
			lda #$21
			sta free_adr			
			; add to block counter
			clc
			lda free_cnt
			adc #$7F
			sta free_cnt
			bcc @nohigh
			inc free_cnt+1
@nohigh:	; restore caller's mem bank
			jsr reset_bank
			pla
			rts

; Allocate one block from Super RAM
; ! Doesnt restore bank !
; OUTPUT:
;	A 			... bank (zero if failed)
;	Y			... address high
; RETURN:
;
md_alloc:	lda free_bank
			bne @goon
			rts
@goon:		; switch bank to free page
			ora #SUPER_RAM_MODE
			sta FE3_REG1
			; remove block from free chain
			jsr get_free
			rts

; Calc index into ptr table for address (high)
; INPUT:
;	A		... address (high)
; OUTPUT:
;	Y		... index
; ! does not switch banks !
;
calc_idx:	sec
			sbc #$20
			bpl @dn_a0
			sbc #$20
@dn_a0:		tay
			rts

; Remove first block from free chain
; OUTPUT:
;	A ... bank
;	Y ... address (high)
; ! does not switch banks !
;
get_free:	lda free_bank
			pha
			beq @oom
			lda free_adr
			pha
			jsr calc_idx
			lda BANK_TABLE_BASE, y
			sta free_bank
			lda ADR_TABLE_BASE, y
			sta free_adr
			lda #0
			sta BANK_TABLE_BASE, y
			sta ADR_TABLE_BASE, y
			lda free_cnt
			bne @nohi
			dec free_cnt+1			
@nohi:		dec free_cnt
			pla
@oom:		tay
			pla
			rts

; Add block from Super RAM to chain
; ! Doesnt restore bank !
; INPUT:
;	A/Y ... last block of chain
; OUTPUT:
;	A/Y ... new block of chain
; RETURN:
;	Carry		... Success (0) or not (1)
;
md_addblk:	ldx free_bank
			beq @fail
			sta blk_cur
			sty blk_cur+1
			stx FE3_REG1 
			; remove free block from chain
			jsr get_free
			sta blk_nxt
			sty blk_nxt+1
			; chain new block to last block in cache
			lda blk_cur
			sta FE3_REG1
			lda blk_cur+1
			jsr calc_idx
			lda blk_nxt
			sta BANK_TABLE_BASE, y
			lda blk_nxt+1
			sta ADR_TABLE_BASE, y
			tay
			lda blk_nxt
			clc
			rts
@fail:		sec
			rts

; Get next page in chain
; ! Doesnt restore bank !
; INPUT:
;	bank (must be current)
;	A 			... address (high)
; OUTPUT:
;	A 			... bank
;	X			... next bank (0 if none)
;	Y 			... address (high)
; RETURN:
;	Carry ... Success (0) or end of chain (1)
;
md_walk:	jsr calc_idx
			lda BANK_TABLE_BASE, y
			beq @is_last
			tax
			pha
			lda ADR_TABLE_BASE, y
			pha
			jsr calc_idx
			stx FE3_REG1
			lda BANK_TABLE_BASE, y
			tax
			pla
			tay
			pla
			clc
			rts
@is_last:	sec
			rts

; Get next page in chain
; INPUT:
;	A			... bank
;	Y 			... address (high)
; OUTPUT:
;	A 			... bank
;	X			... next bank (0 if none)
;	Y 			... address (high)
; RETURN:
;	Carry ... Success (0) or end of chain (1)
;
md_next:	ldx FE3_REG1
			stx save_pg
			sta FE3_REG1
			tya
			jsr md_walk
			pha
			jsr reset_bank
			pla
			rts

; Adds free list to chain
; INPUT:
;	A			... bank
;	Y 			... address (high)
;
add_free:	ldx FE3_REG1
			stx save_pg
			sta FE3_REG1
			tya
			jsr calc_idx
			lda free_bank
			sta BANK_TABLE_BASE, y
			lda free_adr
			sta ADR_TABLE_BASE, y
			jmp reset_bank			

	.segment "CODE"

; Free chain
; INPUT:
;	A			... bank
;	Y 			... address (high)
; OUTPUT:
;	-
; RETURN:
;	-
;
md_free:	cmp #0
			beq @fin
			sta chn_bank
			sty chn_adr
			inc free_cnt
			bne @nxt1
			inc free_cnt+1
@nxt1:		jsr md_next
			bcc @one_more
			ldy chn_adr
			lda chn_bank
			bne @do_free
@one_more:	inc free_cnt
			bne @nxt2
			inc free_cnt+1
@nxt2:		cpx #0
			beq @do_free
			jsr md_next
			bcc @one_more
@do_free:	jsr add_free
			lda chn_bank
			sta free_bank
			lda chn_adr
			sta free_adr
@fin:		rts
